<?php

namespace App\Console\Commands;

use App\Models\Game\Center\Action;
use App\Models\Game\Center\AppSource;
use App\Models\Game\Center\PayOrder;
use App\Models\Game\Center\WithdrawOrder;
use App\Models\Mongo\DailyLog\DayStat;
use App\Models\Mongo\DailyLog\MonthStat;
use Carbon\Carbon;
use Illuminate\Console\Command;

class CalcMonthStat extends Command
{
    protected $signature = 'calc:month-stat {--day= : 请输入运行日期, 例如2021-01-01}';

    protected $description = '每月数据统计';

    /**
     * @var Carbon
     */
    protected $day;

    /**
     * @var Carbon
     */
    protected $monthStart;

    /**
     * @var Carbon
     */
    protected $monthEnd;

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * @param Carbon $carbon
     * @return CalcMonthStat
     */
    public function setDay(Carbon $carbon): CalcMonthStat
    {
        $this->day = $carbon;
        $this->monthStart = $this->day->copy()->startOfMonth();
        $this->monthEnd = $this->day->copy()->endOfMonth();
        return $this;
    }

    public function handle()
    {
        $this->day = $this->option('day') ? Carbon::createFromFormat('Y-m-d', $this->option('day')) : now()->subDay();
        $this->monthStart = $this->day->copy()->startOfMonth();
        $this->monthEnd = $this->day->copy()->endOfMonth();

        // 还没到月末, 防止运行时间偏差导致数量增多, 限制结束时间为查询日期当天
        if ($this->monthEnd->isAfter(now())) {
            $this->monthEnd = $this->day->copy()->endOfDay();
        }

        $yesterdaySpreadInfos = $this->getYesterdaySpreadInfos();
        $payNews = $this->getMonthPayNews();

        $dayStat = new DayStat();
        $appSources = AppSource::get();
        foreach ($appSources as $appSource) {
            $timeRange = [$this->monthStart->toDateString(), $this->monthEnd->toDateString()];

            $monthStat = MonthStat::firstOrNew(['month' => $this->day->format('Y-m'), 'source' => $appSource->id]);
            $sumData = $dayStat->getSumDataBySource($appSource->id, $timeRange);
            $monthStat->fill($sumData);
            $monthStat->payUser = $this->getPayMonthUser($appSource->id);
            list ($monthStat->payNew, $monthStat->payNewUser) = $payNews[$appSource->id] ?? [0, 0]; // 本月新增付费、人数
            $monthStat->withdrawUser = $this->getWithdrawMonthUser($appSource->id); // 本月权益人数 去重
            $monthStat->loginUser = $this->getLoginMonthCount($appSource->id); // 登录人数 去重

            if (!$monthStat->spreadChannel && !$monthStat->spreadType && !$monthStat->spreadUser) {
                $monthStat->spreadChannel = $yesterdaySpreadInfos[$appSource->id]->spreadChannel ?? '';
                $monthStat->spreadType = $yesterdaySpreadInfos[$appSource->id]->spreadType ?? '';
                $monthStat->spreadUser = $yesterdaySpreadInfos[$appSource->id]->spreadUser ?? '';
            }
            $monthStat->save();

            $this->syncToDayStat($monthStat);
        }

        $this->storeMonthStatAll();
    }

    /**
     * 渠道0的数据
     * @return void
     */
    public function storeMonthStatAll()
    {
        $monthStat = new MonthStat();
        $dateString = $this->day->format('Y-m');
        $sumData = $monthStat->getSumDataByMonth($dateString, true);
        // 推广费与修正相同时, 移除修正值, 防止误解
        if ($sumData['cpaCost'] == $sumData['cpaCostFix']) $sumData['cpaCostFix'] = null;

        MonthStat::updateOrCreate(['month' => $dateString, 'source' => 0], $sumData);
    }

    /**
     * 同步部分数据到日报表
     * @param MonthStat $monthStat
     * @return void
     */
    public function syncToDayStat(MonthStat $monthStat)
    {
        $day = $this->day->toDateString();
        // 登录人数
        DayStat::where('day', $day)->where('source', $monthStat->source)->update([
            'monthLoginUser' => $monthStat->loginUser,
        ]);
    }

    /**
     * 本月付费人数
     * @param int $source
     * @return int
     */
    private function getPayMonthUser(int $source): int
    {
        $timeRange = [$this->monthStart->format('Ymd'), $this->monthEnd->format('Ymd')];
        return Action::whereBetween('day', $timeRange)
            ->whereType(Action::TYPE_PAY)
            ->whereSource($source)
            ->distinct('user_id')
            ->count('user_id');
    }

    /**
     * 获取本月新增付费人数
     * @return array
     */
    public function getMonthPayNews(): array
    {
        $timeRange = [$this->monthStart->toDateTimeString(), $this->monthEnd->toDateTimeString()];
        $lists = PayOrder::ofDatetimeRange('payed_at', $timeRange)
            ->ofDatetimeRange('register_at', $timeRange)
            ->selectRaw("source, COUNT(DISTINCT player_id) player, SUM(price) price")
            ->groupBy('source')
            ->get()
            ->keyBy('source');
        $data = [];
        foreach ($lists as $source => $row) {
            $data[$source] = [(int) $row['price'], $row['player'],];
        }
        return $data;
    }
    /**
     * 本月权益用户
     * @param int $source
     * @return int
     */
    private function getWithdrawMonthUser(int $source): int
    {
        $timeRange = [$this->monthStart->toDateTimeString(), $this->monthEnd->toDateTimeString()];
        return WithdrawOrder::ofDatetimeRange('updated_at', $timeRange)
            ->whereStatus(WithdrawOrder::STATUS_SUCCESS)
            ->whereSource($source)
            ->distinct('user_id')
            ->count('user_id');
    }


    /**
     * 本月登录数量
     * @param int $source
     * @return int
     */
    private function getLoginMonthCount(int $source): int
    {
        $timeRange = [$this->monthStart->format('Ymd'), $this->monthEnd->format('Ymd')];
        return Action::whereBetween('day', $timeRange)
            ->whereType(Action::TYPE_LOGIN)
            ->whereSource($source)
            ->distinct('user_id')
            ->count('user_id');
    }

    /**
     * 获取推广信息
     */
    public function getYesterdaySpreadInfos()
    {
        $yesterday = $this->day->copy()->subDay()->toDateString();
        $lists = DayStat::where('day', $yesterday)
            ->get([
                'source',
                'spreadChannel',
                'spreadType',
                'spreadUser'
            ]);

        return $lists->keyBy('source');
    }
}
